package com.cwt.chat.controller;

import com.alibaba.fastjson.JSONObject;
import com.cwt.chat.model.MsgVo;
import com.cwt.chat.model.WeChatLog;
import com.cwt.chat.service.WeChatLogService;
import com.cwt.common.utils.ApplicationContextUtil;
import com.cwt.service.domain.SysUser;
import com.cwt.service.mapper.SysUserMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author chenwt
 * @version V1.0
 * @description 【WebSocket 接口】
 * @since 2023年11月09日 14:29:08
 */
@Component
@ServerEndpoint("/groupChat/{sid}/{userId}")
public class WebSocketServerController {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * 房间号 -> 组成员信息
     */
    private static final ConcurrentHashMap<String, List<Session>> groupMemberInfoMap = new ConcurrentHashMap<>();

    /**
     * 房间号 -> 在线人数， 使用set， 不重复
     */
    private static final ConcurrentHashMap<String, Set<Integer>> onlineUserMap = new ConcurrentHashMap<>();

    /**
     * 收到消息调用的方法， 群成员发送消息
     *
     * @param sid     房间号
     * @param userId  用户id
     * @param message 发送消息
     */
    @OnMessage
    public void onMessage(@PathParam("sid") String sid, @PathParam("userId") Integer userId, String message) {
        // 房间信息
        List<Session> sessionList = groupMemberInfoMap.get(sid);

        // 房间人员信息
        Set<Integer> onlineUserList = onlineUserMap.get(sid);

        // 先一个群组内人员发送消息, 将数据信息转换为 消息model
        MsgVo msg = JSONObject.parseObject(message, MsgVo.class);
        msg.setCount(onlineUserList.size()); // 设置在线人数

        sessionList.forEach(item -> {
            try {
                // 继续转换为消息字符串, 并进行发送
                String msgStr = JSONObject.toJSONString(msg);
                item.getBasicRemote().sendText(msgStr);
                logger.info("【{}】消息发送成功: {}", msg.getUsername(), msgStr);
            } catch (Exception e) {
                logger.error("消息发送异常：{}", e.getMessage());
            }
        });

        // 发送的消息入库
        saveMsgLog(msg, sid);
    }

    /**
     * 建立连接调用的方法， 群人员加入 >> 加入聊天
     *
     * @param session 聊天 session
     * @param sid     房间号
     * @param userId  用户 id
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("sid") String sid, @PathParam("userId") Integer userId) {
        // 根据房间号获取当前的聊天 session 集合
        List<Session> sessionList = groupMemberInfoMap.computeIfAbsent(sid, k -> new ArrayList<>());

        // 根据房间号获取当前房间的聊天人员
        Set<Integer> onlineUserList = onlineUserMap.computeIfAbsent(sid, k -> new HashSet<>());

        // 给当前房间添加人员、和聊天信息
        sessionList.add(session);
        onlineUserList.add(userId);

        // 发送上线通知
        sendInfo(sid, userId, onlineUserList.size(), "上线了~");
    }

    /**
     * 封装用户发送消息， 封装用户数、头像等
     * @param sid           房间号
     * @param userId        用户 id
     * @param onlineSum     在线人数
     * @param info          发送消息
     */
    public void sendInfo(String sid, Integer userId, Integer onlineSum, String info) {
        // 获取该链接用户信息
        SysUser user = ApplicationContextUtil.getApplicationContext().getBean(SysUserMapper.class).selectById(userId);

        // 发送通知
        MsgVo msg = MsgVo.builder()
                .count(onlineSum)
                .userId(userId)
                .username(user.getNickname())
                .avatar("") // 头像
                .msg(user.getNickname() + " : " + info)
                .build();
        onMessage(sid, userId, JSONObject.toJSONString(msg));
    }

    /**
     * 关闭连接调用的方法， 群成员退出、并进行发送通知
     * @param session   当前聊天进程
     * @param sid       房间号
     * @param userId    用户 id
     */
    @OnClose
    public void onClose(Session session, @PathParam("sid") String sid, @PathParam("userId") Integer userId) {
        // 移除进程中的当前进程
        groupMemberInfoMap.get(sid).remove(session);

        // 移除当前在线 人员
        Set<Integer> onlineUserList = onlineUserMap.get(sid);
        onlineUserList.remove(userId);

        // 发送离线通知
        sendInfo(sid, userId, onlineUserList.size(), "下线了~");
    }

    /**
     * 发送消息异常时，进行执行
     * @param error 发送消息异常信息
     */
    @OnError
    public void onError(Throwable error) {
        logger.error("Connection error: {}", error.getMessage());
    }

    /**
     * 聊天的数据信息进行入库
     * @param msg       聊天信息
     * @param roomName  聊天室名称
     */
    public void saveMsgLog(MsgVo msg, String roomName) {
        if (msg.getMsg().contains("上线了~") || msg.getMsg().contains("下线了~")) {
            return;
        }
        WeChatLog chatLog = WeChatLog.builder()
                .roomname(roomName)
                .avatar("assets/img/sculpture/admin.jpeg")
                .userId(msg.getUserId())
                .username(msg.getUsername())
                .nickname(msg.getUsername())
                .msg(msg.getMsg())
                .count(msg.getCount())
                .createTime(new Date())
                .build();

        // 日志入库
        boolean save = ApplicationContextUtil.getApplicationContext().getBean(WeChatLogService.class).save(chatLog);
        if (!save) {
            logger.error("{}：消息【{}】入库失败！", msg.getUsername(), msg.getMsg());
        }
    }

}
